﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace KzwgRoomTester
{

    public partial class MainWindow : Form
    {

        private AccountData account;
        private Session session;
        private Session roomSession;
        private LoggerDumpHandler logDumpHandler;
        private Timer timer;

        private List<Type> igorneMessage;

        private List<PRoleSummary> roleList;
        private PRoleInfo currentRole;

        private Action tickHandler;
        private int tickAcc = 0;


        private Action roomPing;

        private Random random = new Random();

        private bool latestPvpOver = true;
        private int pvpTimes = 0;

        public MainWindow(AccountData account)
        {
            InitializeComponent();
            this.account = account;
            igorneMessage = new List<Type>();
            //igorneMessage.Add(typeof(ScPing));
            //igorneMessage.Add(typeof(ScInputChanged));
            igorneMessage.Add(typeof(ScFrameUpdate));
            logDumpHandler = new LoggerDumpHandler(appendResult);
            timer = new Timer();
        }



        private void Timer_Tick(object sender, EventArgs e)
        {
            tickAcc += 1;
            tickHandler?.Invoke();
        }

        private void MainWindow_Load(object sender, EventArgs e)
        {
            Logger.Setup(logDumpHandler);
            PacketObject.CollectAllRegisteredPackets();
            lbAccID.Text = account.acc_id.ToString();
            lbAccName.Text = account.acc_name;
            lbTaskCount.Text = TaskManager.TaskCount().ToString();
            var server = account.server;

            try
            {
                session = Session.GetTCPSessionInstance(server.host, (ushort)server.port);
                registerDefaultMessageHandler();
                registerDealMessageHandler();
                session.Connect();
                gameLogin();
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
            }
        }

        private void gameLogin()
        {
            var loginData = PacketObject.Create<CsLogin>();

            loginData.accId = account.acc_id;
            loginData.accName = account.acc_name;
            loginData.tick = account.tick;
            loginData.type = 0;

            session.Send(loginData);
        }


        public void appendResult(string formator, params object[] args)
        {
            appendResult(string.Format(formator, args));
        }


        public void appendResult(string result)
        {
            if (txtResult.InvokeRequired)
            {
                txtResult.Invoke(logDumpHandler, result);
            }
            else
            {
                txtResult.AppendText(result);
            }
        }


        private void registerDealMessageHandler()
        {
            registerMessageHandler(typeof(ScLogin), loginPacketHandler);
            registerMessageHandler(typeof(ScEnterGame), enterGamePacketHandler);
            registerMessageHandler(typeof(ScMatchInfo), matchInfoPacketHandler);
            registerMessageHandler(typeof(ScRoleInfo), roleInfoPacketHandler);
            registerMessageHandler(typeof(ScRoomEnter), roomEnterPacketHandler);
            registerMessageHandler(typeof(ScRoomStartLoading), roomStartLoadingPacketHandler);
            registerMessageHandler(typeof(CScRoomNetTest), roomNetTestPacketHandler);
            registerMessageHandler(typeof(ScRoomReward), roomRewardPacketHandler);

        }


        private void roomRewardPacketHandler(PacketObject packet)
        {
            Logger.LogInfo("pvp normal over!");
            roomSession.Disconnect();
            pvpTimes += 1;
            lbPvpTimes.Invoke(new Action(() => lbPvpTimes.Text = pvpTimes.ToString()));
            latestPvpOver = true;
        }

        private void roomStartLoadingPacketHandler(PacketObject packet)
        {
            var loadPacket = PacketObject.Create<CsRoomLoading>();
            loadPacket.progress = 100;
            roomSession.Send(loadPacket);
        }

        private void roomEnterPacketHandler(PacketObject packet)
        {
            this.tickHandler += roomNetTest;
        }

        private void roleInfoPacketHandler(PacketObject packet)
        {
            var scRoleInfo = packet as ScRoleInfo;
            currentRole = scRoleInfo.role;
        }

        private void matchInfoPacketHandler(PacketObject packet)
        {
            var scMatchInfo = packet as ScMatchInfo;
            var room = scMatchInfo.room;
            var roomSess = Session.GetUDPSessionInstance(room.host, room.udpPort);
            roomSess.Connect();
            roomSession = roomSess;
            roomPing = () => sendPing(roomSess, 2, 10);
            this.tickHandler += roomPing;
            this.tickHandler += () => startPvp();
            lbRoomID.Invoke(new Action(() => lbRoomID.Text = room.room_key.ToString()));
            // 进入房间请求
            var enterRoom = PacketObject.Create<CsRoomEnter>();
            enterRoom.roleId = currentRole.roleId;
            enterRoom.roomKey = room.room_key;
            roomSess.Send(enterRoom);

        }


        private void sendPing(Session sess, sbyte type, int fireNum)
        {
            if (tickAcc % fireNum != 0)
            {
                return;
            }
            var p = PacketObject.Create<CsPing>();
            p.type = type;
            try
            {
                sess.Send(p);
            }
            catch (Exception e)
            {
                Logger.LogError("send room ping error: {0}", e.Message);
                if (roomPing != null)
                {
                    this.tickHandler -= roomPing;
                }
            }

        }

        private void enterGamePacketHandler(PacketObject packet)
        {
            startPingTick();
        }

        private void loginPacketHandler(PacketObject packet)
        {
            var scLogin = packet as ScLogin;
            if (scLogin.result == 0)
            {
                //roleList = scLogin.roleList.ToList();
                //cbRoleList.DataSource = roleList;

                cbRoleList.Invoke(new Action(() =>
                {
                    roleList = scLogin.roleList.ToList();
                    foreach (var item in roleList)
                    {
                        cbRoleList.Items.Add(item.name);
                    }

                }));


            }
        }



        #region Regiseter 
        private void registerDefaultMessageHandler()
        {
            var allPackets = PacketObject.GetDefindedPackets();
            foreach (var item in allPackets)
            {
                if (!igorneMessage.Contains(item.Value))
                {
                    registerMessageHandler((ushort)item.Key, defaultPackageHandler);
                }
            }
        }

        private void registerMessageHandler(Type t, PackageHandler handler)
        {
            var packetInfo = Packet.GetPacketInfo(t);
            if (packetInfo != null)
            {
                registerMessageHandler(packetInfo.ID, handler);
            }
        }

        private void registerMessageHandler(ushort ID, PackageHandler handler)
        {
            session.addPackageHandler(ID, handler);

        }
        #endregion

        private void startPingTick()
        {
            this.Invoke(new Action(() =>
            {
                timer.Interval = 3000;
                timer.Tick += Timer_Tick;
                timer.Start();
            }));
            this.tickHandler += () => sendPing(session, 1, 5);

        }

        private void defaultPackageHandler(PacketObject packet)
        {
            Logger.LogInfo("Recv message : {0}", LitJson.JsonMapper.ToJson(packet, false, 0, true));
        }

        private void btnEnter_Click(object sender, EventArgs e)
        {
            var selectName = (string)cbRoleList.SelectedItem;
            if (selectName == null)
            {
                return;
            }

            var role = roleList.Find(a => a.name == selectName);
            var packet = PacketObject.Create<CsEnterGame>();
            lbLv.Text = role.level.ToString();
            lbID.Text = role.roleId.ToString();
            packet.roleId = role.roleId;
            packet.gameVersion = "";
            packet.machineNumber = "";
            packet.producer = "";
            packet.sourceHash = "";
            session.Send(packet);

        }

        private void btnCreateRole_Click(object sender, EventArgs e)
        {

        }

        private void btnPvp_Click(object sender, EventArgs e)
        {
            startPvp();

        }

        private void startPvp()
        {
            if (!latestPvpOver)
            {
                return;
            }
            latestPvpOver = false;
            var packet = PacketObject.Create<CsMatchPvprank>();
            session.Send(packet);
        }


        private static DateTime refTime = DateTime.Now;

        private static UInt32 currentMS()
        {
            var ts = DateTime.Now.Subtract(refTime);
            return (UInt32)ts.TotalMilliseconds;
        }


        private void roomNetTestPacketHandler(PacketObject packet)
        {
            CScRoomNetTest roomNetTest = packet as CScRoomNetTest;
            var time = ByteConverter.ToUInt32(roomNetTest.data, 0);
            lbNetDelay.Invoke(new Action(() => lbNetDelay.Text = (currentMS() - time).ToString()));

        }

        private void roomNetTest()
        {
            try
            {
                CScRoomNetTest roomNetTest = PacketObject.Create<CScRoomNetTest>();
                roomNetTest.data = ByteConverter.GetBytes((UInt32)currentMS());
                roomSession.Send(roomNetTest);
            }
            catch (Exception e)
            {
                Logger.LogError("room net error: {0}", e.Message);
                this.tickHandler -= roomNetTest;
            }

        }


        private void btnEmptyTask_Click(object sender, EventArgs e)
        {
            TaskManager.AddTask(() => { }, 10);
            lbTaskCount.Text = TaskManager.TaskCount().ToString();
        }

        private void btnRandomInput_Click(object sender, EventArgs e)
        {
            CsInputChanged inputChanged = PacketObject.Create<CsInputChanged>();
            //inputChanged.input = random.Next(1, int.MaxValue);
            inputChanged.input = getInput();
            roomSession.Send(inputChanged);
        }

        private int inputIndex = 0;
        private object inputLocker = new object();

        private int getInput()
        {
            lock (inputLocker)
            {
                inputIndex++;
            }
            return inputIndex;
        }

        private void seqInput()
        {
            CsInputChanged inputChanged = PacketObject.Create<CsInputChanged>();

            inputChanged.input = getInput();
            roomSession.Send(inputChanged);
        }

        private void btnSeqInput_Click(object sender, EventArgs e)
        {
            //TaskManager.AddTask(() => seqInput(), 500);
        }
    }
}
